/*********** 初级UI ***********/

/* 定义LED灯引脚 */
const int ledPins[] = {2, 3, 4, 5}; // LED连接的引脚数组
const int numLEDs = 4; // LED的数量

/* 定义按键引脚 0代表vol+，1代表vol- */
const int buttonPins[] = {31, 32};
const int numButtons = 2;

/* 管脚D9连接到蜂鸣器模块的信号脚 */
const int pinBuzzer = 6; 


/*********** 中级UI ***********/

/* ***** 定义数码管引脚 ***** */

// 定义移位寄存器引脚
const int latchPin = 25;  // 连接到74HC595的ST_CP
const int clockPin = 26;  // 连接到74HC595的SH_CP
const int dataPin = 24;   // 连接到74HC595的DS

/* ***** 定义点阵引脚 ***** */

#define DECODE_MODE 9
#define INTENSITY 10
#define SCAN_LIMIT 11
#define SHUTDOWN 12
#define DISPLAY_TEST 15

const int DIN = 51;
const int CS = 52;
const int CLK = 53;

/*********** 定义输入引脚 ***********/ 

// 定义拨码开关引脚
const int switch_array[7] = {7, 8, 9, 10, 11, 12, 13};


/*********** 定义常量 ***********/ 

/* ***** LED灯相关 ***** */

const int blinkDelay = 1000; // 毫秒，LED灯闪烁时间

/* ***** 数码管相关 ***** */

byte digit = 0;          // control pin abcdefgh

// 定义共阴极数码管不同数字对应的逻辑电平的二维数组
// 共阳数码管的段码数组
const byte numberMap[] = {
  B11000000, // 0
  B11111001, // 1
  B10100100, // 2
  B10110000, // 3
  B10011001, // 4
  B10010010, // 5
  B10000010, // 6
  B11111000, // 7
  B10000000, // 8
  B10010000  // 9
};

int num1;
int num2;
int num3;
int num4;

/* ***** 点阵相关 ***** */

// 定义图案逻辑数组
unsigned char pattern[19][8] = {
    {0x3c, 0x3c, 0x34, 0x28, 0x46, 0x38, 0x18, 0x0},      // 0
    {0x0,  0x0,  0x0,  0x7e, 0x0,  0x0,  0x0,  0x0},      // 1
    {0x0,  0x7e, 0x0,  0x0,  0x0,  0xff, 0x0,  0x0},      // 2
    {0x0,  0x7e, 0x0,  0x3c,  0x0,  0xff, 0x0,  0x0},      // 3
    {0x0,  0x7e, 0x5a, 0x6a, 0x6a, 0x46, 0x7e, 0x0},      // 4
    {0x0,  0x7e, 0x10, 0x7c, 0x14, 0x24, 0x7e, 0x0},      // 5
    {0x10, 0x0,  0x7e, 0x0,  0x24, 0x42, 0x0,  0x0},      // 6
    {0x10, 0x10, 0x1e, 0xf0, 0x10, 0x1e, 0x0,  0x0},      // 7
    {0x28, 0x28, 0x24, 0x24, 0x44, 0x42, 0x0,  0x0},      // 8
    {0x0,  0x10, 0x7c, 0x24, 0x24, 0x24, 0x45, 0x6},      // 9
    {0x10, 0x10, 0xff, 0x10, 0x10, 0x10, 0x0,  0x0},      // 10
    {0x7e, 0x14, 0x42, 0x1f, 0x54, 0x5a, 0x0,  0x0},      // 深
    {0x0,  0x10, 0x7e, 0x52, 0x7e, 0x10, 0x10, 0x0},      // 中
    {0x7e, 0x7e, 0x52, 0x5a, 0x52, 0x6e, 0x3c, 0x0},      // 国
    {0x20, 0x2a, 0x76, 0xfc, 0x26, 0x7a, 0xa0, 0x1f},     // 赵
    {0x8,  0x4a, 0x84, 0x6a, 0xee, 0x44, 0x6c, 0x2},      // 钱
    {0x64, 0x44, 0x56, 0xf6, 0x55, 0x44, 0x48, 0x0},      // 孙
    {0x0,  0x7e, 0x34, 0x42, 0x1c, 0xfe, 0x10, 0x10},     // 李
    {0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x0},      // 空
};

bool visited[8];

unsigned char *currentPat;

bool visited_primary = false;

void setup() {
    /* 设置所有LED引脚为输出模式 */
    for (int i = 0; i < numLEDs; i++) {
        pinMode(ledPins[i], OUTPUT);
    }

    /* 设置pinBuzzer脚为输出状态 */
    pinMode(pinBuzzer, OUTPUT); 

    /* 定义接口为输入接口，用于读取按键状态 */
    pinMode(buttonPins[0], INPUT);   
    pinMode(buttonPins[1], INPUT);   

    // Set all the pins of 74HC595 as OUTPUT
    pinMode(latchPin, OUTPUT);
    pinMode(dataPin, OUTPUT);
    pinMode(clockPin, OUTPUT);

    // 配置点阵相关引脚为输出模式
    init_lattice();

    /* ********** 设置所有输入引脚 ********** */

    // 配置所有开关引脚为输入模式
    for(int i = 0; i < 7; i++){
        pinMode(switch_array[i], INPUT_PULLUP);
    }
    // 串口
    Serial1.begin(9600);
}

void loop() {
  if(!visited_primary){
    clear_four_digits();
    display_lattice_pattern(pattern[18]);
    group_order_primary();
    delay(3 * blinkDelay);
  }
  group_order_intermediate();
}

/**
 * 低级UI点名实现
*/
void group_order_primary(){
  /*  */
  /* 四个LED灯全亮，持续三秒*/
  all_High();
  delay(3 * blinkDelay);
  buzz(3 * blinkDelay, 1 * blinkDelay);

  /* 四个LED灯全灭，持续一秒 */
  all_LOW();
  delay(blinkDelay);

  /* 按下实验箱VOL-键，VOL+键不按，代表启动阶段01 */
  while(get_button_state() != 1){}
  buzz(1 * blinkDelay, 1 * blinkDelay);
  buzz(3 * blinkDelay, 1 * blinkDelay);
  
  /* 按下实验箱VOL+键，VOL-键不按，代表点名阶段10 */
  while(get_button_state() != 2){}
  buzz(3 * blinkDelay, 1 * blinkDelay);
  buzz(1 * blinkDelay, 1 * blinkDelay);  

  /* 组号 */
  show_group_number();

  /* 组长 */
  show_first_name_Lea();

  /* 按下VOL-键，表示01，需求员点名 */
  while(get_button_state() != 1){}
  show_first_name_req();

  /* 按下VOL+键，表示10，周志员点名 */
  while(get_button_state() != 2){}
  show_first_name_log();

  /* 同时按下VOL-键和VOL+键，表示11，依次进行优裁员和展示员点名 */
  while(get_button_state() != 3){}
  show_first_name_opt();
  show_first_name_sho();

  /* 同时按下实验箱VOL+键和VOL-键，代表深度课设阶段11 */
  while(get_button_state() != 3){}
  buzz(3 * blinkDelay, 1 * blinkDelay);
  buzz(3 * blinkDelay, 1 * blinkDelay);
  all_High();  

  delay(3 * blinkDelay);
 buzz(200, 100);
 buzz(200, 100);
 buzz(200, 100);

  /* 重启阶段00 */
  while(get_button_state() != 0){}
  all_High();
  delay(blinkDelay);
 buzz(200, 100);
  digitalWrite(ledPins[0], LOW);
  delay(blinkDelay);

 buzz(200, 100);
  digitalWrite(ledPins[1], LOW);
  delay(blinkDelay);

 buzz(200, 100);
  digitalWrite(ledPins[2], LOW);
  delay(blinkDelay);

 buzz(200, 100);
  digitalWrite(ledPins[3], LOW);
  delay(2 * blinkDelay);
  visited_primary = true;
}

// 中低级UI点名实现
void group_order_intermediate(){
    /***********【启动阶段 · Stage0】***********/ 

    // 判别拨码开关初始位置是否正确，若不正确，蜂鸣器提示
    // while(!is_dip_correct() && is_init_visited()){
    //     buzz_warning();
    //     delayMillis(3 * blinkDelay);  // 停顿3秒
    // }
    if(is_init_visited()){
        // 四个LED灯代表的四位bit显示为0001B，代表进入启动阶段
        set_Leds(LOW, LOW, LOW, HIGH);
        currentPat = pattern[18];
        clear_four_digits();
    }

    /***********【点名阶段 · Stage1】***********/ 

    /***********【点名阶段 · 小组摘要】***********/ 
    int switch_state = -1;
    
    for (int i = 6; i >= 0; i--) {
        if (digitalRead(switch_array[i]) == LOW && !visited[i]) {
        switch_state = i;
        break;
        }
    }
    switch (switch_state+1)
    {
    // 拨码开关拨动处于位置“1”的开关，显示组号信息
    // 四个LED灯代表的四位bit显示为0010B
    case 1:
        set_Leds(LOW, LOW, HIGH, LOW);
        show_group_number_open();
        visited[0] = true;
        break;
    // 拨码开关拨动处于位置“2”的开关，显示组长姓氏信息
    case 2:
        set_Leds(LOW, LOW, HIGH, LOW);
        show_first_name_lea_open();
        visited[1] = true;
        break;
    /***********【点名阶段 · 组员点名】***********/ 
    // 四个LED灯代表的四位bit显示为0100B，代表进入点名阶段的组员点名部分   
    // 拨码开关拨动处于位置“3”的开关，显示周志员姓氏信息
    case 3:
        set_Leds(LOW, HIGH, LOW, LOW); 
        show_first_name_req_open();
        visited[2] = true;
        break;  
    // 拨码开关拨动处于位置“4”的开关，显示需求姓氏信息  
    case 4:
        set_Leds(LOW, HIGH, LOW, LOW); 
        show_first_name_log_open();
        visited[3] = true;
        break;
    // 拨码开关拨动处于位置“5”的开关，进行优裁员点名
    case 5:
        set_Leds(LOW, HIGH, LOW, LOW);
        show_first_name_opt_open();
        visited[4] = true;
        break;
    // 拨码开关拨动处于位置“6”的开关，进行展示员点名
    case 6:
        set_Leds(LOW, HIGH, LOW, LOW);
        show_first_name_sho_open();
        visited[5] = true;
        break;

    /***********【深度课设阶段 · Stage2】***********/ 

    // 拨码开关拨动处于位置“7”的开关，进入深度课设阶段
    case 7:
        switch_to_senior_part();
        visited[6] = true;
        break;
    default:
        display_lattice_pattern(currentPat);
        break;
    }    
}

/**
 * author: xxx
 * date: 2023/xx/xx
 * 显示组号的二进制转换格式，组号“XX”转换成8位二进制数为XXXX XXXXB，模板以数字"21"为例，"21"转换成8位二进数为0000 0000B，请按照本组真实组号对本函数进行修改
*/
void show_group_number(){
    /********请在下方区域修改代码及注释，实现利用LED灯显示本组组号二进制形式高4位的功能********/
    set_Leds(LOW, LOW, LOW, HIGH);
    delay(3 * blinkDelay);
    all_LOW();
    delay(blinkDelay);
    /********请在下方区域修改代码及注释，实现利用LED灯显示本组组号二进制形式低4位的功能********/
    digitalWrite(ledPins[0], LOW);
    digitalWrite(ledPins[1], HIGH);
    digitalWrite(ledPins[2], LOW);
    digitalWrite(ledPins[3], HIGH);
    delay(3 * blinkDelay);
    all_LOW();
    delay(blinkDelay);
   buzz(200, 100);
   buzz(200, 100);
}

/**
 * author: xxx
 * date: 2023/xx/xx
 * 显示组长姓氏拼音首字母小写的ASCII码。组长姓氏“XXX”的拼音首字母小写“X”所对应ASCII码为XXXX XXXXB	
*/
void show_first_name_Lea(){
    /********请在下方区域修改代码及注释，实现利用LED灯显示组长姓氏拼音首字母小写的ASCII码高4位的功能********/
    set_Leds(LOW, LOW, LOW, HIGH);
    delay(3 * blinkDelay);
    all_LOW();
    delay(blinkDelay);
    /********请在下方区域修改代码及注释，实现利用LED灯显示组长姓氏拼音首字母小写的ASCII码低4位的功能********/
    set_Leds(LOW, LOW, LOW, HIGH);
    delay(3 * blinkDelay);
    all_LOW();
    delay(blinkDelay);
   buzz(200, 100);
   buzz(200, 100);
}

/**
 * author: xxx
 * date: 2023/xx/xx
 * 显示需求员姓氏拼音首字母小写的ASCII码。需求员姓氏“XXX”的拼音首字母小写“X”所对应ASCII码为XXXX XXXXB
*/
void show_first_name_req(){
    /********请在下方区域编写代码及注释，实现利用LED灯显示需求员姓氏拼音首字母小写的ASCII码高4位的功能********/
    set_Leds(LOW, LOW, HIGH, LOW);
    delay(3 * blinkDelay);
    all_LOW();
    delay(blinkDelay);

    /********请在下方区域编写代码及注释，实现利用LED灯显示需求员姓氏拼音首字母小写的ASCII码低4位的功能*******/
    set_Leds(LOW, LOW, HIGH, LOW);
    delay(3 * blinkDelay);
    all_LOW();
    delay(blinkDelay);
   buzz(200, 100);
   buzz(200, 100);
}

/**
 * author: xxx
 * date: 2023/xx/xx
 * 显示周志员姓氏拼音首字母小写的ASCII码。周志员姓氏“XXX”的拼音首字母小写“X”所对应ASCII码为XXXX XXXXB
*/
void show_first_name_log(){
    /********请在下方区域编写代码及注释，实现利用LED灯显示周志员姓氏拼音首字母小写的ASCII码高4位的功能********/
    set_Leds(LOW, LOW, HIGH, HIGH);
    delay(3 * blinkDelay);
    all_LOW();
    delay(blinkDelay);

    /********请在下方区域编写代码及注释，实现利用LED灯显示周志员姓氏拼音首字母小写的ASCII码低4位的功能********/
    set_Leds(LOW, LOW, HIGH, HIGH);
    delay(3 * blinkDelay);
    all_LOW();
    delay(blinkDelay);
   buzz(200, 100);
   buzz(200, 100);
}

/**
 * author: xxx
 * date: 2023/xx/xx
 * 显示优裁员姓氏拼音首字母的小写的ASCII码。优裁员姓氏“XXX”的拼音首字母小写“X”所对应ASCII码为XXXX XXXXB
*/
void show_first_name_opt(){
    /********请在下方区域编写代码及注释，实现利用LED灯显示优裁员姓氏拼音首字母小写的ASCII码高4位的功能********/
    set_Leds(LOW, HIGH, LOW, LOW);
    delay(3 * blinkDelay);
    all_LOW();
    delay(blinkDelay);

    /********请在下方区域编写代码及注释，实现利用LED灯显示优裁员姓氏拼音首字母小写的ASCII码低4位的功能********/
    set_Leds(LOW, HIGH, LOW, LOW);
    delay(3 * blinkDelay);
    all_LOW();
    delay(blinkDelay);
   buzz(200, 100);
   buzz(200, 100);
}

/**
 * author: xxx
 * date: 2023/xx/xx
 * 显示展示员姓氏拼音首字母小写的ASCII码。展示员姓氏“XXX”的拼音首字母小写“X”所对应ASCII码为XXXX XXXXB
*/
void show_first_name_sho(){
  /********请在下方区域编写代码及注释，实现利用LED灯显示展示员姓氏拼音首字母小写的ASCII码高4位的功能********/
    set_Leds(LOW, HIGH, LOW, HIGH);
    delay(3 * blinkDelay);
    all_LOW();
    delay(blinkDelay);

  /********请在下方区域编写代码及注释，实现利用LED灯显示展示员姓氏拼音首字母小写的ASCII码低4位的功能********/
    set_Leds(LOW, HIGH, LOW, HIGH);
    delay(3 * blinkDelay);
    all_LOW();
    delay(blinkDelay);
   buzz(200, 100);
   buzz(200, 100);
   buzz(200, 100);
}

/***** 中级UI *****/
/**
 * Author：XXX
 * Date：2024/XX/XX
 * Description：显示组号信息。组号"XX"的汉字为"X"，转换为4位四进制数为 XXXXQ
*/
void show_group_number_open(){
    /********请在下方区域修改代码及注释，实现利用点阵和数码管显示本组组号相关信息的功能********/

    // 通过数码管显示该四进制数0022Q
    display_digit_four_number(0, 0, 2, 2);

    // 通过点阵显示组号的汉字“十”
    // display_lattice_pattern(pattern[1]);
    currentPat = pattern[1];

    // 蜂鸣器鸣响0.2秒、关闭0.1秒；再重复一次
    buzz(200, 100);
    buzz(200, 100);
}

/**
 * Author：XXX
 * Date：2024/XX/XX
 * Description：显示组长姓氏信息。组长姓氏的汉字为"X"，该姓氏的拼音首字母小写为"X"，其ASCII码为 XXXX XXXXB，转换成4位四进制数为 XXXXQ
*/
void show_first_name_lea_open(){
    /********请在下方区域修改代码及注释，实现利用点阵和数码管显示组长姓氏相关信息的功能********/

    // 通过数码管显示该四进制数1201Q
    display_digit_four_number(1, 3, 2, 2);

    // 通过点阵显示组号的汉字“赵”
    currentPat = pattern[2];
    // display_lattice_pattern(currentPat); 

    // 蜂鸣器鸣响0.2秒、关闭0.1秒；再重复一次
    buzz(200, 100);
    buzz(200, 100);

    // 向高级UI发送信号
    Serial1.println("SWITCH3");
}

/**
 * Author：XXX
 * Date：2024/XX/XX
 * Description：显示周志员姓氏信息。组长姓氏的汉字为"X"，该姓氏的拼音首字母小写为"X"，其ASCII码为 XXXX XXXXB，转换成4位四进制数为 XXXXQ
*/
void show_first_name_log_open(){
    /********请在下方区域编写代码及注释，实现利用点阵和数码管显示周志员姓氏相关信息的功能********/

    // 通过数码管显示该四进制数1301Q
    display_digit_four_number(1, 3, 0, 1);

    // 通过点阵显示组号的汉字“钱”
    currentPat = pattern[4];
    // display_lattice_pattern(currentPat); 

    // 蜂鸣器鸣响0.2秒、关闭0.1秒；再重复一次
    buzz(200, 100);
    buzz(200, 100);

    // 向高级UI发送信号
    Serial1.println("SWITCH2");
}

/**
 * Author：XXX
 * Date：2024/XX/XX
 * Description：显示需求员姓氏信息。组长姓氏的汉字为"X"，该姓氏的拼音首字母小写为"X"，其ASCII码为 XXXX XXXXB，转换成4位四进制数为 XXXXQ
*/
void show_first_name_req_open(){
    /********请在下方区域编写代码及注释，实现利用点阵和数码管显示需求员姓氏相关信息的功能********/
    
    // 通过数码管显示该四进制数1301Q
    display_digit_four_number(1, 3, 0, 3);

    // 通过点阵显示组号的汉字“孙”
    currentPat = pattern[3];
    // display_lattice_pattern(currentPat); 

    // 蜂鸣器鸣响0.2秒、关闭0.1秒；再重复一次
    buzz(200, 100);
    buzz(200, 100);

    // 向高级UI发送信号
    Serial1.println("SWITCH1");
}

/**
 * Author：XXX
 * Date：2024/XX/XX
 * Description：显示优裁员姓氏信息。组长姓氏的汉字为"X"，该姓氏的拼音首字母小写为"X"，其ASCII码为 XXXX XXXXB，转换成4位四进制数为 XXXXQ
*/
void show_first_name_opt_open(){
    /********请在下方区域编写代码及注释，实现利用点阵和数码管显示优裁员姓氏相关信息的功能********/
    
    // 通过数码管显示该四进制数1301Q
    display_digit_four_number(1, 3, 0, 1);
    // 通过点阵显示组号的汉字“孙”
    currentPat = pattern[5];
    // display_lattice_pattern(currentPat); 

    // 蜂鸣器鸣响0.2秒、关闭0.1秒；再重复一次
    buzz(200, 100);
    buzz(200, 100);

    // 向高级UI发送信号
    Serial1.println("SWITCH4");
}

/**
 * Author：XXX
 * Date：2024/XX/XX
 * Description：显示展示员姓氏信息。组长姓氏的汉字为"X"，该姓氏的拼音首字母小写为"X"，其ASCII码为 XXXX XXXXB，转换成4位四进制数为 XXXXQ
*/
void show_first_name_sho_open(){
    /********请在下方区域编写代码及注释，实现利用点阵和数码管显示展示员姓氏相关信息的功能********/
    
    // 通过数码管显示该四进制数1230Q
    display_digit_four_number(1, 2, 3, 0);
    // 通过点阵显示组号的汉字“李”
    currentPat = pattern[6];
    // display_lattice_pattern(currentPat); 

    // 蜂鸣器鸣响0.2秒、关闭0.1秒；再重复一次
    buzz(200, 100);
    buzz(200, 100);

    // 向高级UI发送信号
    Serial1.println("SWITCH5");
}

/**
 * Author：XXX
 * Date：2024/XX/XX
 * Description：进入深度课设
*/
void switch_to_senior_part(){   
    display_digit_four_number(0, 0, 0, 0);
    // 蜂鸣器鸣响0.2秒、停0.1秒，重复三次
    buzz_warning();
    currentPat = pattern[7];
    // 四个LED灯代表的四位bit显示为1000B，代表进入深度课设阶段
    set_Leds(HIGH, LOW, LOW, LOW);

    // 向高级UI发送信号
    Serial1.println("SWITCH_TO_SENIOR");
}

/* ********** 硬件相关函数 ********** */

/**
 * 四个LED灯全亮
*/
void all_High(){
  for (int i = 0; i < numLEDs; i++) {
    digitalWrite(ledPins[i], HIGH); 
  }    
}

/**
 * 四个LED灯全灭
*/
void all_LOW(){
  for (int i = 0; i < numLEDs; i++) {
    digitalWrite(ledPins[i], LOW); 
  }    
}

/**
 * 获取状态
*/
int get_button_state(){
  if(digitalRead(buttonPins[0]) == LOW && digitalRead(buttonPins[1]) == HIGH){
    return 1;
  }
  else if(digitalRead(buttonPins[0]) == HIGH && digitalRead(buttonPins[1]) == LOW){
    return 2;
  }
  else if(digitalRead(buttonPins[0]) == HIGH && digitalRead(buttonPins[1]) == HIGH){
    return 3;
  }
  else{
    return 0;
  }
}

/**
 * 设置灯泡状态
*/
void set_Leds(int led1, int led2, int led3, int led4){
    digitalWrite(ledPins[0], led1);
    digitalWrite(ledPins[1], led2);
    digitalWrite(ledPins[2], led3);
    digitalWrite(ledPins[3], led4);    
}

/**
 * 判别拨码开关初始位置是否正确，若不正确，蜂鸣器提示
*/
bool is_dip_correct(){
  for (int i = 6; i >= 0; i--) {
    if (digitalRead(switch_array[i]) == LOW) {
      return false;
    }
  }    
  return true;
}

/**
 * 蜂鸣器
*/
void buzz(long toneTime, long noTime){
  // 清屏
  // clear_four_digits();
  
  long frequency = 300; //频率, 单位Hz
  //用tone()函数发出频率为frequency的波形
  tone(pinBuzzer, frequency );
  delay(toneTime); 
  noTone(pinBuzzer);//停止发声
  delay(noTime);
}

/**
 * 警报声
*/
void buzz_warning(){
    buzz(200, 100);
    buzz(200, 100);
    buzz(200, 100);
}

/* ***** 数码管相关函数 ***** */

// 清除数码管显示
void clear_digit() {
  shiftOutByte(0xFF); // 发送11111111，关闭所有段
  digitalWrite(latchPin, HIGH); // 锁存显示
  digitalWrite(latchPin, LOW);
}

// 通过74HC595移位寄存器发送字节
void shiftOutByte(byte data) {
  for (int i = 0; i < 8; ++i) {
    digitalWrite(dataPin, (data & 0x80) != 0); // 发送最高位
    data <<= 1; // 准备发送下一位
    digitalWrite(clockPin, HIGH);
    digitalWrite(clockPin, LOW);
  }
}

// 发送四个数字到数码管
void display_digit_four_number(int num1, int num2, int num3, int num4) {
  clear_digit();
  int nums[4] = {num1, num2, num3, num4};
  for (int i = 0; i < 4; ++i) {
    byte segment = numberMap[nums[3-i]];
    shiftOutByte(segment); // 发送每个数字的段码
    digitalWrite(latchPin, HIGH); // 锁存显示
    digitalWrite(latchPin, LOW);
  }
}

void clear_four_digits(){
  for (int i = 0; i < 4; ++i) {
    clear_digit();
  }  
}

/* ***** 点阵相关函数 ***** */

/**
 * 点阵初始化设置
*/
void init_lattice(){
  pinMode(CS, OUTPUT);
  pinMode(CLK, OUTPUT);
  pinMode(DIN, OUTPUT);

  // 初始化MAX7219寄存器
  setReg(DISPLAY_TEST, 0x00); // 关闭显示测试
  setReg(DECODE_MODE, 0x00);  // 关闭BCD译码
  setReg(SCAN_LIMIT, 0x07);   // 设置扫描限制为8（全部8行）
  setReg(INTENSITY, 0x08);    // 设置亮度等级
  setReg(SHUTDOWN, 0x01);     // 关闭休眠模式
}

// 数据写入函数
void setReg(byte address, byte value) {
  digitalWrite(CS, LOW);
  putData(address);
  putData(value);
  digitalWrite(CS, HIGH);
}

// 写入字节函数
void putData(byte data) {
  for (byte i = 8; i != 0; --i) {
    digitalWrite(CLK, LOW);
    digitalWrite(DIN, (data & 0x80) ? HIGH : LOW); // 将最高位写入DIN
    data <<= 1; // 左移数据，准备下一位的写入
    digitalWrite(CLK, HIGH);
  }
}

/**
 * 点阵显示汉字
*/
void display_lattice_pattern(unsigned char *pat){
    for (int i = 0; i < 8; i++) {
        setReg(i + 1, pat[i]); // MAX7219的寄存器从1开始对应8行
    }    
}

/**
 * 判断是否都没访问过
*/
bool is_init_visited(){
    for(int i = 0; i < 8; i++){
        if(visited[i]){
            return false;
        }
    }
    return true;
}
